package com.hk.common.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.*;
import redis.clients.jedis.params.SetParams;

import java.io.*;
import java.util.*;

/**
 * redis工具类
 * @author  zhangyu
 * @date 2019-11-04
 */
@Component
public class Redis {

    private final String appName = "pow_";

    @Autowired
    private JedisPool jedisPool;

    Logger logger = LoggerFactory.getLogger(Redis.class);

    class ObjectTransCoder<T extends Serializable> {
        public byte[] serialize(Object value) {
            if (value == null) {
                throw new NullPointerException("Can't serialize null");
            }
            byte[] result = null;
            try (ByteArrayOutputStream bos =new ByteArrayOutputStream();
                 ObjectOutputStream os= new ObjectOutputStream(bos)){
                T object = (T) value;
                os.writeObject(object);
                os.close();
                bos.close();
                result = bos.toByteArray();
            } catch (IOException e) {
                throw new IllegalArgumentException("Non-serializable object", e);
            }
            return result;
        }

        public T deserialize(byte[] in) {
            T result = null;
            try ( ByteArrayInputStream bis = new ByteArrayInputStream(in);
                  ObjectInputStream is = new ObjectInputStream(bis);){
                if (in != null) {
                    result = (T) is.readObject();
                    is.close();
                    bis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }


    public void set(String key, String value)  {
        try (Jedis jedis = jedisPool.getResource()){
            jedis.set(appName+key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 带有效时间的 time秒
     *
     * @param key
     * @param value
     * @param time
     * @throws Exception
     */
    public void set(String key, String value, int time){
        try (Jedis jedis = jedisPool.getResource()){
            boolean keyExist = jedis.exists(appName+key);
            if (keyExist) {
                jedis.del(appName+key);
            }
            SetParams params = new SetParams();
            jedis.set(appName+key,value,params.nx().ex(time));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void del(String key)  {
        try (Jedis jedis = jedisPool.getResource()){
            boolean keyExist = jedis.exists(appName+key);
            if (keyExist) {
                jedis.del(appName+key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String get(String key) {
        try (Jedis jedis = jedisPool.getResource()){
            return jedis.get(appName+key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public <T extends Serializable> void setObject(String key,T o,int time) {
        try (Jedis jedis = jedisPool.getResource()){
            boolean keyExist = jedis.exists(appName+key);
            if (keyExist) {
                jedis.del(appName+key);
            }
            ObjectTransCoder<T> objectTransCoder = new ObjectTransCoder();
            //NX  只有不存在时才存储
            SetParams params = new SetParams();
            jedis.set((appName+key).getBytes(),objectTransCoder.serialize(o),params.nx().ex(time));
        } catch (Exception e) {
            logger.error("Set key:"+key+" error : " + e);
        }
    }


    public void setObject(String key,Serializable o) {
        try (Jedis jedis = jedisPool.getResource()){
            ObjectTransCoder<?> objectTransCoder = new ObjectTransCoder<>();
            boolean keyExist = jedis.exists(appName+key);
            if (keyExist) {
                jedis.del(appName+key);
            }
            jedis.set((appName+key).getBytes(),objectTransCoder.serialize(o));
        } catch (Exception e) {
            logger.error("Set key:"+key+" error : " + e);
        }
    }

    /**
     * 获取list
     *
     * @param <>
     * @param key
     * @return list
     */
    public <T extends Serializable> T getObject(String key) {
        try (Jedis jedis = jedisPool.getResource()){
            if (jedis == null || !jedis.exists((appName+key).getBytes())) {
                return null;
            }
            byte[] in = jedis.get((appName+key).getBytes());
            ObjectTransCoder<T> objectTransCoder = new ObjectTransCoder();
            T object = (T) objectTransCoder.deserialize(in);
            return object;
        } catch (Exception e) {
            logger.error("getList key error : " + e);
            return null;
        }
    }

    /**
     * keys 方法使用scan 代替
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern){
        List<String> strList = scan(pattern);
        return new HashSet<>(strList);
    }

    public void batchDelKey(String pattern) {
        try (Jedis jedis = jedisPool.getResource()) {
            List<String> strList = scan(pattern);
            String[] a = new String[strList.size()];
            jedis.del(strList.toArray(a));
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("batchDelKey error : " + e);
        }
    }


    /**
     * scan 实现
     *
     * @param pattern  表达式
     */
    public List<String> scan(String pattern) {

        List<String> keysList = new ArrayList<>();
        try (Jedis jedis = jedisPool.getResource()) {
            ScanParams params = new ScanParams().match(pattern).count(Integer.MAX_VALUE);
            String cursor = "0";

            do {
                ScanResult<String> scanResult = jedis.scan(cursor, params);
                List<String> selectedElements = scanResult.getResult();
                if (selectedElements != null && !selectedElements.isEmpty())
                    keysList.addAll(selectedElements);
                cursor = scanResult.getCursor();
                System.out.println("after the scan-action, the cursor equals : " + cursor);
            } while (!"0".equals(cursor));

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("batchDelKey error : " + e);
        }

        return keysList;
    }


    /**
     * 构建key带前缀的key
     */
    public String prefixKey(String prefix, String key){
        return prefix +
                key;
    }


    /**
     * 发布消息
     */
    public void publish(String channel,String msg){
        try (Jedis jedis = jedisPool.getResource()){
            jedis.publish(channel,msg);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("publish error : " + e);
        }
    }

    /**
     * 订阅消息 psubscribe strings可以是模糊匹配的 chanel*
     */
    public void psubscribe(JedisPubSub sub, String... strings){
        try (Jedis jedis = jedisPool.getResource()){
            jedis.psubscribe(sub,strings);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
    }

    /**
     * 订阅消息 subscribe strings 必须是明确的
     */
    public void subscribe(JedisPubSub sub,String... strings){
        try (Jedis jedis = jedisPool.getResource()){
            jedis.subscribe(sub,strings);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
    }


    /** 添加hash表 不存在将会自动添加
     * @param key
     * @param field
     * @param value
     * @return
     * @Author zhouxiaoran
     */
    public void hSet( String key, String field, String value) {
        try (Jedis jedis = jedisPool.getResource()){
            jedis.hset(appName+key, field, value);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
    }
    /**
     * 判断hash中 指定的field是否存在
     * @param key
     * @param field
     * @return result
     * @Author zhouxiaoran
     */
    public boolean ifExist(String key, String field) {
        boolean result = false;
        try (Jedis jedis = jedisPool.getResource()){
            result = jedis.hexists(appName+key, field);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
        return result;
    }
    /**
     * 获取hash中 所有的field value
     * @param key
     * @return
     * @Author zhouxiaoran
     */
    public Map<String, String> hGetAll(String key) {
        Map<String, String> result = null;
        try (Jedis jedis = jedisPool.getResource()){
            result = jedis.hgetAll(appName+key);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
        return result;
    }
    /**
     * 获取hash中 指定的field的值
     * @param key
     * @param field
     * @return
     * @Author zhouxiaoran
     */
    public List<String> hGet(String key, String... field) {
        List<String> result = null;
        try (Jedis jedis = jedisPool.getResource()){
            result = jedis.hmget(appName+key, field);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
        return result;
    }

    /**
     * 删除hash中 field对应的值
     * @param key
     * @param field
     * @return
     * @Author zhouxiaoran
     */
    public void hDelete(String key, String... field) {
        try (Jedis jedis = jedisPool.getResource()){
            jedis.hdel(appName+key, field);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("subscribe error : " + e);
        }
    }

}
